package nachos.userprog;

import nachos.machine.*;

import nachos.threads.*;
import nachos.userprog.*;
import nachos.vm.VMKernel;

import java.io.EOFException;
import java.util.*;

/**
 * Encapsulates the state of a user process that is not contained in its
 * user thread (or threads). This includes its address translation state, a
 * file table, and information about the program being executed.
 *
 * <p>
 * This class is extended by other classes to support additional functionality
 * (such as additional syscalls).
 *
 * @see	nachos.vm.VMProcess
 * @see	nachos.network.NetProcess
 */
public class UserProcess {
    /**
     * Allocate a new process.
     */
    public UserProcess() {
/*	int numPhysPages = Machine.processor().getNumPhysPages();
	pageTable = new TranslationEntry[numPhysPages];
	for (int i=0; i<numPhysPages; i++)
	    pageTable[i] = new TranslationEntry(i,i, true,false,false,false);*/
    /*jill add begin*/
    openfiles = new OpenFile[maxFiles];
    openfiles[0] = UserKernel.console.openForReading();
    openfiles[1] = UserKernel.console.openForWriting();
    fs = UserKernel.fileSystem;
    /*jill add end*/ 
    }
  
    /**
     * Allocate and return a new process of the correct class. The class name
     * is specified by the <tt>nachos.conf</tt> key
     * <tt>Kernel.processClassName</tt>.
     *
     * @return	a new process of the correct class.
     */
    public static UserProcess newUserProcess() {
	return (UserProcess)Lib.constructObject(Machine.getProcessClassName());
    }

    /**
     * Execute the specified program with the specified arguments. Attempts to
     * load the program, and then forks a thread to run it.
     *
     * @param	name	the name of the file containing the executable.
     * @param	args	the arguments to pass to the executable.
     * @return	<tt>true</tt> if the program was successfully executed.
     */
    public boolean execute(String name, String[] args) {
	if (!load(name, args))
	    return false;
	/*jill add*/
	thread = new UThread(this);
	thread.setName(name).fork();
    semaphore.P();
    pid = counter++;
    processCount++;
    semaphore.V();
    /*jill end*/
	return true;
    }

    /**
     * Save the state of this process in preparation for a context switch.
     * Called by <tt>UThread.saveState()</tt>.
     */
    public void saveState() {
    }

    /**
     * Restore the state of this process after a context switch. Called by
     * <tt>UThread.restoreState()</tt>.
     */
    public void restoreState() {
	Machine.processor().setPageTable(pageTable);
    }

    /**
     * Read a null-terminated string from this process's virtual memory. Read
     * at most <tt>maxLength + 1</tt> bytes from the specified address, search
     * for the null terminator, and convert it to a <tt>java.lang.String</tt>,
     * without including the null terminator. If no null terminator is found,
     * returns <tt>null</tt>.
     *
     * @param	vaddr	the starting virtual address of the null-terminated
     *			string.
     * @param	maxLength	the maximum number of characters in the string,
     *				not including the null terminator.
     * @return	the string read, or <tt>null</tt> if no null terminator was
     *		found.
     */
    public String readVirtualMemoryString(int vaddr, int maxLength) {
	Lib.assertTrue(maxLength >= 0);

	byte[] bytes = new byte[maxLength+1];

	int bytesRead = readVirtualMemory(vaddr, bytes);

	for (int length=0; length<bytesRead; length++) {
	    if (bytes[length] == 0)
		return new String(bytes, 0, length);
	}

	return null;
    }

    /**
     * Transfer data from this process's virtual memory to all of the specified
     * array. Same as <tt>readVirtualMemory(vaddr, data, 0, data.length)</tt>.
     *
     * @param	vaddr	the first byte of virtual memory to read.
     * @param	data	the array where the data will be stored.
     * @return	the number of bytes successfully transferred.
     */
    public int readVirtualMemory(int vaddr, byte[] data) {
	return readVirtualMemory(vaddr, data, 0, data.length);
    }

    /**
     * Transfer data from this process's virtual memory to the specified array.
     * This method handles address translation details. This method must
     * <i>not</i> destroy the current process if an error occurs, but instead
     * should return the number of bytes successfully copied (or zero if no
     * data could be copied).
     *
     * @param	vaddr	the first byte of virtual memory to read.
     * @param	data	the array where the data will be stored.
     * @param	offset	the first byte to write in the array.
     * @param	length	the number of bytes to transfer from virtual memory to
     *			the array.
     * @return	the number of bytes successfully transferred.
     */
    public int readVirtualMemory(int vaddr, byte[] data, int offset,
				 int length) {
	Lib.assertTrue(offset >= 0 && length >= 0 && offset+length <= data.length);

	byte[] memory = Machine.processor().getMemory();
	/*jill add begin*/
	int vpn = vaddr/pageSize,tlbIndex;
	if  (vpn < 0 || vpn > pageTable.length-1)
		return 0;
	tlbIndex = checkTlb(vaddr);
//	pin(vpn);
	int paddr = pageTable[vpn].ppn*pageSize + vaddr%pageSize;
	int pageSizeLeft = pageTable[vpn].ppn*pageSize + pageSize - paddr; 
	int lengthToRead = length;
	int amount,totalAmount = 0;
	do
	{
		amount = Math.min(lengthToRead, pageSizeLeft);
		System.arraycopy(memory, paddr, data, offset, amount);
		totalAmount += amount;
		offset += amount;
		lengthToRead -= amount;
		if (lengthToRead == 0)
		{
			changeAccessStatus(vpn,tlbIndex,false);
			unPin(vpn);
			break;
		}
		changeAccessStatus(vpn,tlbIndex,false);
		unPin(vpn);
		vpn++;
		if (vpn>pageTable.length-1)
		{
			break;
		}
		tlbIndex = checkTlb(vpn*pageSize);
//		pin(vpn);
		paddr = pageTable[vpn].ppn*pageSize;
		pageSizeLeft = pageSize;
	}
	while(true);
	/*jill add end*/
	return totalAmount;
    }

    /**
     * Transfer all data from the specified array to this process's virtual
     * memory.
     * Same as <tt>writeVirtualMemory(vaddr, data, 0, data.length)</tt>.
     *
     * @param	vaddr	the first byte of virtual memory to write.
     * @param	data	the array containing the data to transfer.
     * @return	the number of bytes successfully transferred.
     */
    public int writeVirtualMemory(int vaddr, byte[] data) {
	return writeVirtualMemory(vaddr, data, 0, data.length);
    }

    /**
     * Transfer data from the specified array to this process's virtual memory.
     * This method handles address translation details. This method must
     * <i>not</i> destroy the current process if an error occurs, but instead
     * should return the number of bytes successfully copied (or zero if no
     * data could be copied).
     *
     * @param	vaddr	the first byte of virtual memory to write.
     * @param	data	the array containing the data to transfer.
     * @param	offset	the first byte to transfer from the array.
     * @param	length	the number of bytes to transfer from the array to
     *			virtual memory.
     * @return	the number of bytes successfully transferred.
     */
    public int writeVirtualMemory(int vaddr, byte[] data, int offset,
				  int length) {
	Lib.assertTrue(offset >= 0 && length >= 0 && offset+length <= data.length);

	byte[] memory = Machine.processor().getMemory();

	/*jill add begin*/
	int vpn = vaddr/pageSize;
	int tlbIndex;
	if  (vpn < 0 || vpn > pageTable.length-1)
		return 0;
	tlbIndex = checkTlb(vaddr);
	if (pageTable[vpn].readOnly)
	{
		unPin(vpn);
		return 0;
	}
	
	int paddr = pageTable[vpn].ppn*pageSize + vaddr%pageSize;
	int pageSizeLeft = pageTable[vpn].ppn*pageSize + pageSize - paddr; 
	int lengthToRead = length;
	int amount,totalAmount = 0;
	do
	{
		amount = Math.min(lengthToRead, pageSizeLeft);
		System.arraycopy(data, offset, memory, paddr, amount);
		totalAmount += amount;
		offset += amount;
		lengthToRead -= amount;
		if (lengthToRead == 0)
		{
			changeAccessStatus(vpn,tlbIndex,true);
			unPin(vpn);
			break;
		}
		changeAccessStatus(vpn,tlbIndex,true);
		unPin(vpn);
		vpn++;
		if (vpn>pageTable.length-1)
		{
			break;
		}
		tlbIndex = checkTlb(vpn*pageSize);
		if (pageTable[vpn].readOnly)
		{
			unPin(vpn);
			break;
		}	
		paddr = pageTable[vpn].ppn*pageSize;
		pageSizeLeft = pageSize;
	}
	while(true);
	/*jill add end*/
	return totalAmount;
    }

    /**
     * Load the executable with the specified name into this process, and
     * prepare to pass it the specified arguments. Opens the executable, reads
     * its header information, and copies sections and arguments into this
     * process's virtual memory.
     *
     * @param	name	the name of the file containing the executable.
     * @param	args	the arguments to pass to the executable.
     * @return	<tt>true</tt> if the executable was successfully loaded.
     */
    private boolean load(String name, String[] args) {
	Lib.debug(dbgProcess, "UserProcess.load(\"" + name + "\")");
	
	OpenFile executable = ThreadedKernel.fileSystem.open(name, false);
	if (executable == null) {
	    Lib.debug(dbgProcess, "\topen failed");
	    return false;
	}

	try {
	    coff = new Coff(executable);
	}
	catch (EOFException e) {
	    executable.close();
	    Lib.debug(dbgProcess, "\tcoff load failed");
	    return false;
	}

	// make sure the sections are contiguous and start at page 0
	numPages = 0;
	for (int s=0; s<coff.getNumSections(); s++) {
	    CoffSection section = coff.getSection(s);
	    if (section.getFirstVPN() != numPages) {
		coff.close();
		Lib.debug(dbgProcess, "\tfragmented executable");
		return false;
	    }
	    numPages += section.getLength();
	}

	// make sure the argv array will fit in one page
	byte[][] argv = new byte[args.length][];
	int argsSize = 0;
	for (int i=0; i<args.length; i++) {
	    argv[i] = args[i].getBytes();
	    // 4 bytes for argv[] pointer; then string plus one for null byte
	    argsSize += 4 + argv[i].length + 1;
	}
	if (argsSize > pageSize) {
	    coff.close();
	    Lib.debug(dbgProcess, "\targuments too long");
	    return false;
	}

	// program counter initially points at the program entry point
	initialPC = coff.getEntryPoint();	

	// next comes the stack; stack pointer initially points to top of it
	numPages += stackPages;
	initialSP = numPages*pageSize;

	// and finally reserve 1 page for arguments
	numPages++;

	if (!loadSections())
	    return false;

	// store arguments in last page
	int entryOffset = (numPages-1)*pageSize;
	int stringOffset = entryOffset + args.length*4;

	this.argc = args.length;
	this.argv = entryOffset;
	
	for (int i=0; i<argv.length; i++) {
	    byte[] stringOffsetBytes = Lib.bytesFromInt(stringOffset);
	    Lib.assertTrue(writeVirtualMemory(entryOffset,stringOffsetBytes) == 4);
	    entryOffset += 4;
	    Lib.assertTrue(writeVirtualMemory(stringOffset, argv[i]) ==
		       argv[i].length);
	    stringOffset += argv[i].length;
	    Lib.assertTrue(writeVirtualMemory(stringOffset,new byte[] { 0 }) == 1);
	    stringOffset += 1;
	}

	return true;
    }

    /**
     * Allocates memory for this process, and loads the COFF sections into
     * memory. If this returns successfully, the process will definitely be
     * run (this is the last step in process initialization that can fail).
     *
     * @return	<tt>true</tt> if the sections were successfully loaded.
     */
    protected boolean loadSections() {
	if (numPages > Machine.processor().getNumPhysPages()) {
	    coff.close();
	    Lib.debug(dbgProcess, "\tinsufficient physical memory");
	    return false;
	}
	/*jill add begin*/
	pageTable = new TranslationEntry[numPages];
	Lock lock = UserKernel.lock;
	List<Integer> list = UserKernel.freePages;
	lock.acquire();
	if (numPages>list.size())
	{
		lock.release();
		return false;
	}
	for (int i = 0;i<numPages;i++)
	{
		pageTable[i] = new TranslationEntry(i,list.remove(0),
				 true,false,false,false);

	}
	lock.release();
	/*jill add end*/
	// load sections
	for (int s=0; s<coff.getNumSections(); s++) {
	    CoffSection section = coff.getSection(s);
	    
	    Lib.debug(dbgProcess, "\tinitializing " + section.getName()
		      + " section (" + section.getLength() + " pages)");

	    for (int i=0; i<section.getLength(); i++) {
		int vpn = section.getFirstVPN()+i;
		// for now, just assume virtual addresses=physical addresses
		/*jill begin*/
		pageTable[vpn].readOnly = section.isReadOnly();
		section.loadPage(i,pageTable[vpn].ppn);		
		/*jill end*/
	    }
	}
	return true;
    }

    /**
     * Release any resources allocated by <tt>loadSections()</tt>.
     */
    protected void unloadSections() {
    }    

    /**
     * Initialize the processor's registers in preparation for running the
     * program loaded into this process. Set the PC register to point at the
     * start function, set the stack pointer register to point at the top of
     * the stack, set the A0 and A1 registers to argc and argv, respectively,
     * and initialize all other registers to 0.
     */
    public void initRegisters() {
	Processor processor = Machine.processor();

	// by default, everything's 0
	for (int i=0; i<processor.numUserRegisters; i++)
	    processor.writeRegister(i, 0);

	// initialize PC and SP according
	processor.writeRegister(Processor.regPC, initialPC);
	processor.writeRegister(Processor.regSP, initialSP);

	// initialize the first two argument registers to argc and argv
	processor.writeRegister(Processor.regA0, argc);
	processor.writeRegister(Processor.regA1, argv);
    }

    /**
     * Handle the halt() system call. 
     */
    private int handleHalt() {
/*jill*/
    if (UserKernel.root != this)
    	return 0;
	Machine.halt();
	
	Lib.assertNotReached("Machine.halt() did not halt machine!");
	return 0;
    }
/*jill add begin*/
    private int nextFileDesc()
    {
    	for(int i = 0;i<maxFiles;i++)
    	{
    		if (openfiles[i] == null)
    		{
    			return i;
    		}
    	}
    	return -1;
    }
    
    public TranslationEntry[] getPageTable()
    {
    	return pageTable;
    }
    /*
    public UserProcess findProcess(int pid)
    {
    	Stack<UserProcess> s = new Stack<UserProcess>();
    	s.push(this);
    	UserProcess process;
    	int i;
    	while(!s.empty())
    	{
    		process = s.pop();
    		if (process.pid !=pid)
    			return process;
    		else
    		{
    			for(i=0;i<process.children.size();i++)
    			{
    				s.push(process.children.get(i));
    			}
    		}
    	}
    	return null;
    }
*/
    private int handleCreate(int a0) {
    if (a0 < 0 || a0/pageSize > pageTable.length-1)
    	return -1;
	String str = readVirtualMemoryString(a0,256);
	if (str == null||str.isEmpty())
		return -1;
/*	int alreadyOpen = isAlreadyOpen(str);
	if (alreadyOpen != -1)
		return alreadyOpen;*/
    int nextFd = nextFileDesc();
    if (nextFd == -1)
    	return -1;
	OpenFile of = fs.open(str,true);
	if (of == null)
		return -1;
	else
	{
		openfiles[nextFd] = of;
		return nextFd;
	}
    }
    private int handleOpen(int a0) {
    if (a0 < 0 || a0/pageSize > pageTable.length-1)
        return -1;
	String str = readVirtualMemoryString(a0,256);
	if (str  == null||str.isEmpty())
		return -1;
	/*
	int alreadyOpen = isAlreadyOpen(str);
	if (alreadyOpen != -1)
		return alreadyOpen; */
    int nextFd = nextFileDesc();
    if (nextFd == -1)
    	return -1;
	OpenFile of = fs.open(str,false);
	if (of == null)
		return -1;
	else
	{
		openfiles[nextFd] = of;
		return nextFd;
	}
    }
    private int handleRead(int a0,int a1,int a2)
    {
    	if (a0<0 || a0>15)
    		return -1;
    	if (openfiles[a0] == null)
    		return -1;
    	if (a1 <0 || a1/pageSize > pageTable.length-1)
    		return -1;
    	if (a2 < 0)
    		return -1;
    	byte[] buf  = new byte[bufferSize];
    	OpenFile of  = openfiles[a0];
    	int amountNeed = 0;
    	int amountRead = 0;
    	int totalRead = 0;
    	int left = a2;
    	int address = a1;
    	while(left>0)
    	{
    		amountNeed = Math.min(left, bufferSize);
    		amountRead = of.read(buf,0,amountNeed);
    		amountRead = writeVirtualMemory(address,buf,0,amountRead);
    		totalRead += amountRead;
    		if (amountRead < amountNeed)
    			break;
        	address += amountRead;
    		left -= amountRead;   	
    	}
    	return totalRead;
    }
    private int handleWrite(int a0,int a1,int a2)
    {
    	if (a0<0 || a0>15)
    		return -1;
    	if (openfiles[a0] == null)
    		return -1;
    	if (a1 < 0 || a1/pageSize > pageTable.length-1)
    		return -1;
    	if (a2 < 0)
    		return -1;
    	byte[] buf  = new byte[bufferSize];
    	OpenFile of  = openfiles[a0];
    	int amountNeed = 0;
    	int amountWrite = 0;
    	int totalWrite = 0;
    	int left = a2;
    	int address = a1;
    	while(left>0)
    	{
    		amountNeed = Math.min(left, bufferSize);
    		amountWrite = readVirtualMemory(address,buf,0,amountNeed);
    		amountWrite = of.write(buf,0,amountWrite);
    		totalWrite += amountWrite;
    		if (amountWrite < amountNeed)
    			break;
        	address += amountWrite;
    		left -= amountWrite;   	
    	}
    	return totalWrite;
    }
    private int handleClose(int a0)
    {
    	if ( a0<0 || a0>15 || openfiles[a0] == null)
    		return -1;    		
    	openfiles[a0].close();
    	openfiles[a0] = null;
    	return 0;
    }
    private int handleUnlink(int a0)
    {
        if (a0 < 0 || a0/pageSize > pageTable.length-1)
        	return -1;
    	String str = readVirtualMemoryString(a0,256);
    	if (str  == null)
    		return -1;
    	if (fs.remove(str))
    		return 0;
    	else
    		return -1;
    }
    private int handleExec(int a0,int a1,int a2)
    {
    	String str = readVirtualMemoryString(a0,256);
    	if (!str.endsWith(".coff"))
    		return -1;
    	String[] argv = new String[a1];
    	String argument;
    	int argumentOfAddress;
    	byte [] pointer = new byte[4];
    	boolean success = false;
    	int address = a2;
    	for (int i = 0;i<a1;i++)
    	{
    		readVirtualMemory(address,pointer);
    		argumentOfAddress = Lib.bytesToInt(pointer,0);
    		argument = readVirtualMemoryString(argumentOfAddress,256);
    		if (argument != null)
    		{
    			argv[i] = argument;
    			address += 4; 
    		}
    		else
    			return -1;
    	}
    	UserProcess process = UserProcess.newUserProcess();   	
    	success = process.execute(str,argv);
    	if (success)
    	{
    		this.children.add(process);
/*    		process.parent = this; */
    		return process.pid;
    	}
    	else
    		return -1;
    }
    private int handleJoin(int a0,int a1)
    {
    	UserProcess process = null;
    	for (UserProcess p: children)
    	{
    		if (p.pid == a0)
    		{
    			process = p;
    			break;
    		}
    	}
    	if (process == null)
    		return -1;
    	if (process.hasExit==false)
    	{
    		/*
    		boolean intStatus = Machine.interrupt().disable();	
       		process.joinedBy = KThread.currentThread();
    		KThread.currentThread().sleep();
    		Machine.interrupt().restore(intStatus);
    		*/
    		process.thread.join();
    	}
    	writeVirtualMemory(a1,Lib.bytesFromInt(process.status));
    	children.remove(process);
/*    	process.parent = null; */
    	if (process.hasError)
    		return 0;
    	else
    		return 1;
    }
    protected int handleExit(int a0)
    {
    	for(int i = 0;i<maxFiles;i++)
    	{
    		if (openfiles[i]!=null)
    			openfiles[i].close();
    	}
    	/*
    	for(UserProcess process:children)
    	{
    		process.parent = null;
    	}
    	*/
    	Lock lock = UserKernel.lock;
    	List<Integer> freePages= UserKernel.freePages;
    	lock.acquire();
    	for(int i = 0;i<numPages;i++)
    	{
    		if (pageTable[i].valid)
    		{
    			freePages.add(pageTable[i].ppn);
    			pageTable[i].valid = false;
    		}
    	}
    	lock.release();
    	children.clear();
    	this.hasExit = true;
    	this.status = a0;
/*    	if (this.joinedBy != null) 
    	{
    		boolean intStatus = Machine.interrupt().disable();
    		this.joinedBy.ready();
    		Machine.interrupt().restore(intStatus);
    	} */
    	unloadSections();
    	semaphore.P();
    	processCount--;
    	if (processCount == 0)
    	{
    		semaphore.V();
    		Kernel.kernel.terminate();
    	}
    	semaphore.V();
    	
    	KThread.currentThread().finish();
    	return 0;
    }
/*jill add end*/
    private static final int
        syscallHalt = 0,
	syscallExit = 1,
	syscallExec = 2,
	syscallJoin = 3,
	syscallCreate = 4,
	syscallOpen = 5,
	syscallRead = 6,
	syscallWrite = 7,
	syscallClose = 8,
	syscallUnlink = 9;

    /**
     * Handle a syscall exception. Called by <tt>handleException()</tt>. The
     * <i>syscall</i> argument identifies which syscall the user executed:
     *
     * <table>
     * <tr><td>syscall#</td><td>syscall prototype</td></tr>
     * <tr><td>0</td><td><tt>void halt();</tt></td></tr>
     * <tr><td>1</td><td><tt>void exit(int status);</tt></td></tr>
     * <tr><td>2</td><td><tt>int  exec(char *name, int argc, char **argv);
     * 								</tt></td></tr>
     * <tr><td>3</td><td><tt>int  join(int pid, int *status);</tt></td></tr>
     * <tr><td>4</td><td><tt>int  creat(char *name);</tt></td></tr>
     * <tr><td>5</td><td><tt>int  open(char *name);</tt></td></tr>
     * <tr><td>6</td><td><tt>int  read(int fd, char *buffer, int size);
     *								</tt></td></tr>
     * <tr><td>7</td><td><tt>int  write(int fd, char *buffer, int size);
     *								</tt></td></tr>
     * <tr><td>8</td><td><tt>int  close(int fd);</tt></td></tr>
     * <tr><td>9</td><td><tt>int  unlink(char *name);</tt></td></tr>
     * </table>
     * 
     * @param	syscall	the syscall number.
     * @param	a0	the first syscall argument.
     * @param	a1	the second syscall argument.
     * @param	a2	the third syscall argument.
     * @param	a3	the fourth syscall argument.
     * @return	the value to be returned to the user.
     */
    public int handleSyscall(int syscall, int a0, int a1, int a2, int a3) {
	switch (syscall) {
	case syscallHalt:
	    return handleHalt();
/*jill add begin*/    
	case syscallCreate:
		return handleCreate(a0);
	
	case syscallOpen:
		return handleOpen(a0);
		
	case syscallRead:
		return handleRead(a0,a1,a2);

	case syscallWrite:
		return handleWrite(a0,a1,a2);

	case syscallClose:
		return handleClose(a0);
		
	case syscallUnlink:
		return handleUnlink(a0);
	case syscallExit:
		return handleExit(a0);
	case syscallExec:
		return handleExec(a0,a1,a2);
	case syscallJoin:
		return handleJoin(a0,a1);
/*jill add end*/
	default:
		/*jill add*/
	    this.hasError = true;
	    handleExit(errorCode);
        /*jill end*/
	    Lib.debug(dbgProcess, "Unknown syscall " + syscall);
	    Lib.assertNotReached("Unknown system call!");
	}
	return 0;
    }
/*jill add*/
    protected int checkTlb(int vaddr)
    {
    	return 0;
    }
    protected void changeAccessStatus(int vpn,int tlbIndex,boolean write)
    {}
    protected void unPin(int vpn)
    {}
/*jill end*/
    /**
     * Handle a user exception. Called by
     * <tt>UserKernel.exceptionHandler()</tt>. The
     * <i>cause</i> argument identifies which exception occurred; see the
     * <tt>Processor.exceptionZZZ</tt> constants.
     *
     * @param	cause	the user exception that occurred.
     */
    public void handleException(int cause) {
	Processor processor = Machine.processor();

	switch (cause) {
	case Processor.exceptionSyscall:
	    int result = handleSyscall(processor.readRegister(Processor.regV0),
				       processor.readRegister(Processor.regA0),
				       processor.readRegister(Processor.regA1),
				       processor.readRegister(Processor.regA2),
				       processor.readRegister(Processor.regA3)
				       );
	    processor.writeRegister(Processor.regV0, result);
	    processor.advancePC();
	    break;				       
				       
	default:
		/*jill add*/
	    this.hasError = true;
	    handleExit(errorCode);
        /*jill end*/
	    Lib.debug(dbgProcess, "Unexpected exception: " +
		      Processor.exceptionNames[cause]);
	    Lib.assertNotReached("Unexpected exception");

	}
    }

    /** The program being run by this process. */
    protected Coff coff;

    /** This process's page table. */
    protected TranslationEntry[] pageTable;
    /** The number of contiguous pages occupied by the program. */
    protected int numPages;

    /** The number of pages in the program's stack. */
    protected final int stackPages = 8;
    /*jill add begin*/
    protected OpenFile[] openfiles;
	protected FileSystem fs;
	private final int bufferSize = 1024;
	private final int maxFiles = 16;
	protected final int errorCode = -1;
	private static int counter = 1;
	private static int processCount = 0;
	protected int pid;
	private static Semaphore semaphore = new Semaphore(1);
	private List<UserProcess> children= new LinkedList<UserProcess>();
/*	private UserProcess parent = null;*/
	private int status;
	private boolean hasExit = false;
	protected boolean hasError = false;
	private KThread thread = null;
    /*jill add end*/    
    private int initialPC, initialSP;
    private int argc, argv;
    private static final int pageSize = Processor.pageSize;
    private static final char dbgProcess = 'a';
}
